/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package TabuSearch;

import java.io.*;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.biojava.bio.structure.AminoAcid;
import org.biojava.bio.structure.Atom;
import org.biojava.bio.structure.Chain;
import org.biojava.bio.structure.Structure;
import pdb.PDBReader;
import pdb.PDBWrite;
import rotlib.RotamerLibrary;
import rotlib.RotamerLibraryCollection;
import rotlib.io.RotamerLibraryReader;
import utils.*;

/**
 *
 * @author Valkyrie
 */
public class TabuSearch {

    final static String DATETIME = (new SimpleDateFormat("dd-MM-yyyy_HHmmss").format(new Date()));
    static public final String Separator = System.getProperty("file.separator");
    static public final String PathAlgorithm = System.getProperty("user.dir")
            + Separator
            + "Algorithm"
            + Separator
            + "Statistics"
            + Separator
            + "Structures"
            // + DATETIME
            + Separator;
    public static RotamerLibrary rl;
    public static Random rnd = new Random(System.currentTimeMillis());
    public static ArrayList ListaTabuS = new ArrayList();
    public static ArrayList ListaTabuM = new ArrayList();
    public static AminoacidsGrid tempGrid = new AminoacidsGrid();
    public static AllRotamerInteractions ari;
//    public static HashMap<Integer, AminoacidExtended> aminoacidMap;
    public static RotamerLibraryCollection rlc = RotamerLibraryReader.readRotamerLibrary(RotamerLibraryCollection.DUN_IND_ROT_LIB_TYPE);
    public static double maxvdw = 0;
    public static double maxcRatio = 0;
    public static double maxrot = 0;
    public static double minvdw = 0;
    public static double mincRatio = 0;
    public static double minrot = 0;
    public static double promvdw = 0;
    public static double promcRatio = 0;
    public static double promrot = 0;
    public static double cont = 0;
    public static double interval = 6;
    public static double TotEnergyOld, TotEnergyNew,auxEnergy=50000,auxRot1=-1,auxRot2=-1;

    @SuppressWarnings("CallToThreadDumpStack")
    public static void TS(Solution sol, Structure s, RotamerLibraryCollection rlc, boolean efficientColl) {

        ListaTabuS = new ArrayList();
        int nResiduos = getSeqResLength(s);
        int nGeneraciones = 2;//nResiduos*2;
        int tabuListSize = nResiduos / 4;
        int nGensWOImp = 0;

        int nChains = s.getChains().size();
        int residue = 0, chain = 0, AminoAcidIndex = 0;
        AminoAcid aa;
        int[] _1a2j = {-1, 17, 4, 22, 3, -1, 33, 24, 0, 0, 2, 7, 22, 64, 1, 1, -1, -1, -1, 1,
            28, 1, 4, 14, 4, 0, 1, 4, 4, 1, 1, 8, 2, 2, 29, 2, 26, 26, 1, 6,
            8, 8, 2, 7, 13, 1, 58, 55, 55, 7, 1, 22, -1, 1, 64, 11, 2, 58, 5, 5,
            2, 2, 0, 20, -1, -1, 7, 4, -1, 47, 7, 7, 2, 17, -1, 3, -1, 1, -1, 22,
            -1, 7, -1, 2, 20, 8, 52, 2, 2, 1, 0, 7, 4, 26, -1, 1, 30, 55, 0, 30,
            0, 7, 57, 0, -1, 1, 7, 7, 33, 7, 1, 4, 7, 10, -1, -1, 7, 55, -1, 22,
            13, 2, 7, -1, -1, 3, 13, 0, 2, 1, 1, 55, 2, 6, 1, -1, 29, 30, 14, 38,
            -1, -1, -1, 7, 2, 29, 3, 60, -1, 2, 0, -1, 13, 4, 1, 14, -1, 31, 4, 15,
            8, 9, 0, 29, -1, 21, 4, 0, 0, 9, 25, 8, 1, 2, 1, 13, 17, 2, -1, 7, 2, 1, 55, 2, 7, 2, 22, 56};

        Object[] obj = new Object[5];

        tempGrid.obtainAminoacids(s, sol);
        tempGrid.createGrid(tempGrid.aminoacidMap.values(), interval);//Creamos el Grid
        ari = new AllRotamerInteractions(s, rlc, tempGrid, sol);
        
        TotEnergyOld = AllRotamerInteractions.getTotalEnergyEfficient(sol);
        ari.calculateSelfEnergies();
        System.out.println("\nTotal eficient energy con el grid Best: " + (TotEnergyOld+AllRotamerInteractions.totalSelfEnergy));
        
        ari.DEE();
//        Object keys[] = AllRotamerInteractions.enabledRotamers.keySet().toArray();
//        boolean keepsTheCorrectOne;
//        for (int x = 0; x < keys.length; x++) {
//            String enabledRots = "";
//            for (int y = 0; y < AllRotamerInteractions.enabledRotamers.get(Integer.parseInt(keys[x].toString())).size(); y++) {
//                enabledRots += AllRotamerInteractions.enabledRotamers.get(Integer.parseInt(keys[x].toString())).get(y) + ", ";
//            }
////            AllRotamerInteractions.enabledRotamers.get(Integer.parseInt(keys[x].toString())).add(_1a2j[Integer.parseInt(keys[x].toString())]);
//            System.out.println("Enabled rotamers of " + keys[x] + ": " + enabledRots+" -- Contiene a "+_1a2j[Integer.parseInt(keys[x].toString())]+"? ");
//        }
        
        
        //<editor-fold defaultstate="collapsed" desc="Comprobar que los vecinos "j" en el gris de "i" contengan a i en sus vecinos cercanos">
//        ArrayList<Integer> vecinosDeI=new ArrayList<>();
//        ArrayList<Integer> vecinosDeJ=new ArrayList<>();
//        Object[] keysn=sol.getRomaterVector().get(0).keySet().toArray(new Integer[0]);
//        Arrays.sort(keysn);
//        int num=0;
//        for(int xx=0;xx<keysn.length;xx++){
//            num=Integer.parseInt(keysn[xx].toString());
//            vecinosDeI=AllRotamerInteractions.AAsToVerify.get(num);
//            for(Integer j:vecinosDeJ){
//                vecinosDeJ=AllRotamerInteractions.AAsToVerify.get(j);
//                if(!vecinosDeJ.contains(28)){
//                    System.out.println("Fuuu "+j+" no contiene a "+num);
//                }
//            }}
        //</editor-fold>
        
        
//        AminoAcid ax1=AllRotamerInteractions.aGrid.getAminoacid(33),ax2=AllRotamerInteractions.aGrid.getAminoacid(96);
//        RotamerLibrary rlx=rlc.getRotamerLibrary(ax1.getPDBName());
//        ax1=StructureUtil.createAminoacid(ax1, rlx.getRotamer().get(4));
//        rlx=rlc.getRotamerLibrary(ax2.getPDBName());
//        ax2=StructureUtil.createAminoacid(ax2, rlx.getRotamer().get(29));
//        System.out.println("Energía D: "+AllRotamerInteractions.calculateEnergyBetweenSCSCWRL4(ax1,ax2)+" - "+ari.getInteraction(33, 4, 96, 29)+" : "+ari.getInteraction(96, 29, 33, 4));
//        System.out.println("Energía D: "+ari.getInteraction(33, 4, 96, 29));
        
        AllRotamerInteractions.sol=AllRotamerInteractions.setMaxProbEnabled(sol);
//        AllRotamerInteractions.sol=AllRotamerInteractions.setBestSCBBProbRots(sol);

        String rotamers = "";
        for (int l = 0; l < AllRotamerInteractions.sol.getRomaterVector().get(0).size(); l++) {
            rotamers += l + ".- " + AllRotamerInteractions.sol.getRomaterVector().get(0).get(l) + "\t";
        }
        System.out.println(rotamers);
        /////////////////////////
        try {
            for (int i = 0; i < nGeneraciones; i++) {
                chain = rnd.nextInt(nChains);
                residue = 0;//rnd.nextInt(/*s.getChain(chain).getSeqResLength()*/ nResiduos - 1);
                
                Object[] keys=sol.getRomaterVector().get(0).keySet().toArray(new Integer[0]);
                Arrays.sort(keys);
                int res=0;
                for (int key = residue; key < residue + nResiduos; key++) {
                    res=Integer.parseInt(keys[key].toString());
                    //***//***//***//***//***//***//
                    try{
                    if(AllRotamerInteractions.aGrid.getAminoacid(res)==null){
                        i--;
                        continue;
                    }
                    }catch(Exception e){i--;
                        continue;}
                    aa = AllRotamerInteractions.aGrid.getAminoacid(res);//(AminoAcid) s.getChain(chain).getAtomGroup(res);
//                    System.out.println("Residuo seleccionado:"+res+" AA: "+aa.getPDBCode());
                    AminoAcidIndex = Arrays.binarySearch(ListUtil.THREE_LETTER_CODE, aa.getPDBName());

                    if ((AminoAcidIndex != 7 && AminoAcidIndex != 0)) {
                        if (!ListaTabuS.contains(res)) {
                            obj = getBestNeighbor(chain, res, aa, AllRotamerInteractions.sol, rlc);// energyByAminoacid);

//                            if (ListaTabuS.contains(res)) {
//                                if ((Boolean) obj[1]) {
//                                    //System.out.println("Colisiones antes = " + sol.getAminoacidsCollisions().size() + "Energía : " + sol.energy);
//                                    //                    bestEnergy = (Double) obj[1];
//                                    AllRotamerInteractions.sol.getRomaterVector().get(chain).put(res, obj[0].toString());
//                                    AllRotamerInteractions.sol = actualizarAminoacidos(AllRotamerInteractions.sol, (HashMap<Integer, Integer>) obj[3]);
//                                    //                    sol.getTotalEnergy();
//                                    //System.out.println("Colisiones Después = " + sol.getAminoacidsCollisions().size() + "Energía : " + sol.energy);
//                                    nGensWOImp = 0;
//                                }
//                            } else {
                                ListaTabuS.add(res);
                                if ((Boolean) obj[1]) {
                                    //                    bestEnergy = (Double) obj[1];
                                    AllRotamerInteractions.sol.getRomaterVector().get(chain).put(res, obj[0].toString());
                                    AllRotamerInteractions.Rotamers.put(res, Integer.parseInt(obj[0].toString()));
                                    AllRotamerInteractions.sol = actualizarAminoacidos((Solution) AllRotamerInteractions.sol, (HashMap<Integer, Integer>) obj[3]);
                                    
                                    //copiar ari.sol a ari.Rotamers
                                    Object[] keysz=sol.getRomaterVector().get(0).keySet().toArray(new Integer[0]);
                                    Arrays.sort(keysz);
                                    int keyz;
                                    for(int c=0;c<sol.getRomaterVector().get(0).size();c++){
                                        keyz=Integer.parseInt(keysz[c].toString());
                                        AllRotamerInteractions.Rotamers.put(keyz, Integer.parseInt(AllRotamerInteractions.sol.getRomaterVector().get(0).get(keyz)));
                                    }
                                    
                                    nGensWOImp = 0;
                                }
//                            }
                            if (!(Boolean) obj[1]) {
                                nGensWOImp++;
                            }
//                            if ((nGensWOImp >= nResiduos)/* && (tabuListSize == nResiduos / 4)*/) {
////                                tabuListSize *= 2;
////                                nGensWOImp = 0;
//                                i=Integer.MAX_VALUE;
//                            }

                            if (ListaTabuS.size() >= tabuListSize) {
                                ListaTabuS.remove(ListaTabuS.get(0));
                                tabuListSize = nResiduos / 4;
                            }
                        }
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("NULLL!!");
        }
//        rotamers = "";
//        for (int l = 0; l < AllRotamerInteractions.sol.getRomaterVector().get(0).size(); l++) {
//            if(sol.getAAVector().get(0).get(l).equalsIgnoreCase("ALA") || sol.getAAVector().get(0).get(l).equalsIgnoreCase("GLY")){
//                rotamers+="**";
//            }
//            rotamers += l + ".- " + AllRotamerInteractions.sol.getRomaterVector().get(0).get(l) + "\t";
//        }
        
        System.out.println("\n" + rotamers);
//        sol.getRomaterVector().get(0).set(1, "17");
//        sol.getRomaterVector().get(0).set(2, "4");
//        sol.getRomaterVector().get(0).set(4, "3");
//        sol.getRomaterVector().get(0).set(6, "33");
//        sol.getRomaterVector().get(0).set(7, "24");
//        sol.getRomaterVector().get(0).set(9, "0");
//        sol.getRomaterVector().get(0).set(10, "2");
//        sol.getRomaterVector().get(0).set(13, "64");
//        sol.getRomaterVector().get(0).set(14, "1");
//        sol.getRomaterVector().get(0).set(20, "28");
//        sol.getRomaterVector().get(0).set(22, "4");
//        sol.getRomaterVector().get(0).set(23, "14");
//        sol.getRomaterVector().get(0).set(25, "0");
//        sol.getRomaterVector().get(0).set(26, "1");
//        sol.getRomaterVector().get(0).set(27, "4");
//        sol.getRomaterVector().get(0).set(28, "2");
//        sol.getRomaterVector().get(0).set(30, "1");
//        sol.getRomaterVector().get(0).set(35, "2");
//        sol.getRomaterVector().get(0).set(37, "26");
//        sol.getRomaterVector().get(0).set(39, "6");
//        sol.getRomaterVector().get(0).set(41, "8");
//        sol.getRomaterVector().get(0).set(42, "2");
//        sol.getRomaterVector().get(0).set(43, "7");
//        sol.getRomaterVector().get(0).set(44, "13");
//        sol.getRomaterVector().get(0).set(46, "8");
//        sol.getRomaterVector().get(0).set(47, "55");
//        sol.getRomaterVector().get(0).set(48, "18");
//        sol.getRomaterVector().get(0).set(50, "1");
//        sol.getRomaterVector().get(0).set(52, "22");
//        sol.getRomaterVector().get(0).set(54, "64");
//        sol.getRomaterVector().get(0).set(55, "11");
//        sol.getRomaterVector().get(0).set(56, "2");
//        sol.getRomaterVector().get(0).set(57, "58");
//        sol.getRomaterVector().get(0).set(58, "5");
//        sol.getRomaterVector().get(0).set(59, "5");
//        sol.getRomaterVector().get(0).set(60, "2");
//        sol.getRomaterVector().get(0).set(61, "2");
//        sol.getRomaterVector().get(0).set(62, "0");
//        sol.getRomaterVector().get(0).set(63, "20");
//        sol.getRomaterVector().get(0).set(66, "7");
//        sol.getRomaterVector().get(0).set(67, "4");
//        sol.getRomaterVector().get(0).set(69, "47");
//        sol.getRomaterVector().get(0).set(72, "2");
//        sol.getRomaterVector().get(0).set(73, "17");
//        sol.getRomaterVector().get(0).set(79, "22");
//        sol.getRomaterVector().get(0).set(81, "7");

//        sol.getRomaterVector().get(0).put(71, "56");
//        sol.getRomaterVector().get(0).put(168, "4");
//        sol.getRomaterVector().get(0).set(23, "22");

//        for(int i=0;i<_1a2j.length;i++){
//            AllRotamerInteractions.sol.getRomaterVector().get(0).set(i, _1a2j[i]+"");
//        }

        System.out.println("total energy con el grid después: " + (AllRotamerInteractions.getTotalEnergyEfficient(AllRotamerInteractions.sol)+AllRotamerInteractions.totalSelfEnergy));
        
//        Object[] keys=sol.getRomaterVector().get(0).keySet().toArray(new Integer[0]);
//        Arrays.sort(keys);
//        int key;
//        for(int c=0;c<sol.getRomaterVector().get(0).size();c++){
//            key=Integer.parseInt(keys[c].toString());
//            if(!AllRotamerInteractions.sol.getRomaterVector().get(0).get(key).equals(AllRotamerInteractions.Rotamers.get(key).toString())){
//                System.out.println("Diferencia en: "+key+" - "+AllRotamerInteractions.sol.getRomaterVector().get(0).get(key)+":"+AllRotamerInteractions.Rotamers.get(key));
//            }
//        }
        
//        ArrayList<Integer> nearAAs=AllRotamerInteractions.AAsToVerify.get(1);
//        AminoAcid am1,am2;
//        double e1=0,e2=0,e1bb=0,e2bb=0;
//        am1=AllRotamerInteractions.aGrid.getAminoacid(1);
//        rl=rlc.getRotamerLibrary(am1.getPDBName());
//        
//        for(int n=4;n<35;n++){
//            
//            am1=StructureUtil.createAminoacid(am1,rl.getRotamer().get(4));
//            for(int j:nearAAs){
//                am2=AllRotamerInteractions.aGrid.getAminoacid(j);
//                e1bb+= AllRotamerInteractions.energySCBB(am1,am2);
//                e2bb+= AllRotamerInteractions.energySCBBRASP(am1,am2);
//                if(am2.getPDBName().equalsIgnoreCase("ALA") || am2.getPDBName().equalsIgnoreCase("GLY"))
//                    continue;
//                e1+= AllRotamerInteractions.calculateEnergyBetweenSC(am1,am2);
//                e2+= AllRotamerInteractions.calculateEnergyBetweenSCRASP(am1,am2);
//            }
//            System.out.println("\nrot "+n);
//            System.out.println("\tEnergy cisrr: "+e1+" + "+e1bb);
//            System.out.println("\tEnergy rasp: "+e2+" + "+e2bb);
//        }
        
    }

    @SuppressWarnings("CallToThreadDumpStack")
    public static Object[] getBestNeighbor(int chain, int residue, AminoAcid aa, Solution sol, RotamerLibraryCollection rlc) {//, ArrayList<HashMap<Integer, AminoacidInteraction>> energyByAminoacid) {
        Object[] obj = new Object[5];
//        double bestEnergy = sol.getTotalEnergy();
        rl = rlc.getRotamerLibrary(aa.getPDBName());
        RotamerLibrary rl2 = null;
        AminoAcid aa1, aa2, aa22;
        boolean collision = false;
        int aminoAcidIndex = 0;
        int keyPdbCodeAA1 = residue;
        int keyPdbCodeAA2 = 0;
        HashMap<Integer, Integer> rotameros = new HashMap<>();
        obj[1] = Boolean.FALSE;
        boolean change1 = false;
        boolean change2 = false;
        ArrayList<Integer> aminoacidsToVerify = new ArrayList<>();
        double newSelfE1 = 0, newSelfE2 = 0, newTotSelfE;

        double a1InteractionsOld, a2InteractionsOld, a1InteractionsNew, a2InteractionsNew;
        ArrayList<Integer> rotsEnabled1, rotsEnabled2;
        int rot, rot2;
////////////////////////
        aminoacidsToVerify = AllRotamerInteractions.AAsToVerify.get(residue);
//        aminoacidsToVerify.add(residue);      
////////////////////////
        
        rotsEnabled1 = AllRotamerInteractions.enabledRotamers.get(residue);
        for (int rotTemp = 0; rotTemp < rotsEnabled1.size(); rotTemp++) {
            rot = rotsEnabled1.get(rotTemp);

            aa1 = StructureUtil.createAminoacid(aa, rl.getRotamer().get(rot));
            actualizarGrid(aa, aa1, rot);

            for (int i = 0; i < aminoacidsToVerify.size(); i++) {

                aa2 = AllRotamerInteractions.aGrid.getAminoacid(aminoacidsToVerify.get(i));// estaba getAminoacid(i) y funcionaba bien D:
                aminoAcidIndex = Arrays.binarySearch(ListUtil.THREE_LETTER_CODE, aa2.getPDBName());
                if (aminoAcidIndex != 7 && aminoAcidIndex != 0) {
                    
                    keyPdbCodeAA2 = Integer.parseInt(aa2.getPDBCode())-1;
                    
                    rl2 = rlc.getRotamerLibrary(aa2.getPDBName());

                    if (StructureUtil.isThereCollision(aa1, aa2)) {
                            
                        collision = true;
                        change1 = false;
//                     
                        a1InteractionsOld = AllRotamerInteractions.AAInteractions.get(keyPdbCodeAA1);

                        rotsEnabled2 = AllRotamerInteractions.enabledRotamers.get(keyPdbCodeAA2);
                        for (int rotTemp2 = 0; rotTemp2 < rotsEnabled2.size(); rotTemp2++) {

                            rot2 = rotsEnabled2.get(rotTemp2);
                            
                            aa22 = StructureUtil.createAminoacid(aa2, rl2.getRotamer().get(rot2));
                            
                            a2InteractionsOld = AllRotamerInteractions.AAInteractions.get(keyPdbCodeAA2);
                            
                            actualizarGrid(aa2, aa22, rot2);
                           
                            a2InteractionsNew = AllRotamerInteractions.getAminoacidInteractions(keyPdbCodeAA2);
                            
                            a1InteractionsNew = AllRotamerInteractions.getAminoacidInteractions(keyPdbCodeAA1);

                            newSelfE1 = AllRotamerInteractions.aGrid.getRotamerFrequencyRASP(aa1.getPDBName(), rot);
                            newSelfE2 = AllRotamerInteractions.aGrid.getRotamerFrequencyRASP(aa2.getPDBName(), rot2);
                            
//                            if(a1InteractionsOld != 0 && AllRotamerInteractions.getInteraction(keyPdbCodeAA1, AllRotamerInteractions.oldRotamers.get(keyPdbCodeAA1), keyPdbCodeAA2, AllRotamerInteractions.oldRotamers.get(keyPdbCodeAA2)) !=0){
//                                if(!(a2InteractionsOld!=0 && AllRotamerInteractions.getInteraction(keyPdbCodeAA1, AllRotamerInteractions.oldRotamers.get(keyPdbCodeAA1), keyPdbCodeAA2, AllRotamerInteractions.oldRotamers.get(keyPdbCodeAA2)) !=0 && a2InteractionsOld >= AllRotamerInteractions.getInteraction(keyPdbCodeAA1, AllRotamerInteractions.oldRotamers.get(keyPdbCodeAA1), keyPdbCodeAA2, AllRotamerInteractions.oldRotamers.get(keyPdbCodeAA2)))){
//                                    System.out.println("WTF dude D:");
//                                    System.out.println("\nAminoácido: "+keyPdbCodeAA2+" Rot1: "+rot+" Rot2: "+rot2+" Rotamers1:"+AllRotamerInteractions.Rotamers.get(keyPdbCodeAA1)+" Rotamers2:"+AllRotamerInteractions.Rotamers.get(keyPdbCodeAA2)+" oldRotamers1: "+AllRotamerInteractions.oldRotamers.get(keyPdbCodeAA1)+" oldRotamers2: "+AllRotamerInteractions.oldRotamers.get(keyPdbCodeAA2));
//                                    System.out.println(a2InteractionsOld+" - "+AllRotamerInteractions.getInteraction(keyPdbCodeAA1, AllRotamerInteractions.oldRotamers.get(keyPdbCodeAA1), keyPdbCodeAA2, AllRotamerInteractions.oldRotamers.get(keyPdbCodeAA2))+" - "+a2InteractionsOld+" - "+a1InteractionsOld+"\n-*-*-*-*-*-*-*-");
//                                }
//                            }
                            
                            TotEnergyNew = TotEnergyOld - a1InteractionsOld - (a2InteractionsOld - AllRotamerInteractions.getInteraction(keyPdbCodeAA1, AllRotamerInteractions.oldRotamers.get(keyPdbCodeAA1), keyPdbCodeAA2, AllRotamerInteractions.oldRotamers.get(keyPdbCodeAA2))) + a1InteractionsNew + (a2InteractionsNew - AllRotamerInteractions.getInteraction(keyPdbCodeAA1, rot, keyPdbCodeAA2, rot2));
                            newTotSelfE = AllRotamerInteractions.totalSelfEnergy - AllRotamerInteractions.selfEnergies.get(keyPdbCodeAA1) - AllRotamerInteractions.selfEnergies.get(keyPdbCodeAA2) + newSelfE1 + newSelfE2;
                                                        
                            //******************//
//                            TotEnergyNew=AllRotamerInteractions.getTotalEnergyEfficient();
//                            System.out.println(TotEnergyNew+" + " + newTotSelfE+" < "+TotEnergyOld+" + " + AllRotamerInteractions.totalSelfEnergy+" -- VS "+(AllRotamerInteractions.getTotalEnergyEfficient()+newTotSelfE));
                            if ((TotEnergyNew + (newTotSelfE*0.35)) < (TotEnergyOld + (AllRotamerInteractions.totalSelfEnergy*0.35)) ) {
                                System.out.println(TotEnergyNew+" = "+TotEnergyOld+"-"+a1InteractionsOld+"-("+a2InteractionsOld+" - "+AllRotamerInteractions.getInteraction(keyPdbCodeAA1, AllRotamerInteractions.oldRotamers.get(keyPdbCodeAA1), keyPdbCodeAA2, AllRotamerInteractions.oldRotamers.get(keyPdbCodeAA2))+")+"+a1InteractionsNew+"+("+a2InteractionsNew+" - "+AllRotamerInteractions.getInteraction(keyPdbCodeAA1, rot, keyPdbCodeAA2, rot2)+")"+" en "+keyPdbCodeAA1+" y "+keyPdbCodeAA2+" -- VS "+(AllRotamerInteractions.getTotalEnergyEfficient()+newTotSelfE));
                                if((AllRotamerInteractions.getTotalEnergyEfficient()+newTotSelfE)>auxEnergy || (TotEnergyNew + newTotSelfE+30) < (TotEnergyOld + AllRotamerInteractions.totalSelfEnergy)){
                                    System.out.println(((AllRotamerInteractions.getTotalEnergyEfficient()+newTotSelfE)>auxEnergy)+" - "+((TotEnergyNew + newTotSelfE+30) < (TotEnergyOld + AllRotamerInteractions.totalSelfEnergy)));
                                    System.out.println("---"+TotEnergyNew+" = "+TotEnergyOld+"-"+a1InteractionsOld+"-("+a2InteractionsOld+" - "+AllRotamerInteractions.getInteraction(keyPdbCodeAA1, AllRotamerInteractions.oldRotamers.get(keyPdbCodeAA1), keyPdbCodeAA2, AllRotamerInteractions.oldRotamers.get(keyPdbCodeAA2))+")+"+a1InteractionsNew+"+("+a2InteractionsNew+" - "+AllRotamerInteractions.getInteraction(keyPdbCodeAA1, rot, keyPdbCodeAA2, rot2)+")"+" en "+keyPdbCodeAA1+" y "+keyPdbCodeAA2);
                                }
                                if(keyPdbCodeAA1==2 && keyPdbCodeAA2==183){
                                    System.out.println(" **** DESPUES ***");
                                    ari.getTotalEnergyEfficientDetailed();
                                    System.out.println("\n\n");
                                }
//                                if(a1InteractionsNew == AllRotamerInteractions.getInteraction(keyPdbCodeAA1, rot, keyPdbCodeAA2, rot2) && a2InteractionsNew < AllRotamerInteractions.getInteraction(keyPdbCodeAA1, rot, keyPdbCodeAA2, rot2)){
//                                    System.out.println("\nAminoácido: "+keyPdbCodeAA2+" Rot1: "+rot+" Rot2: "+rot2+" Rotamers1:"+AllRotamerInteractions.Rotamers.get(keyPdbCodeAA1)+" Rotamers2:"+AllRotamerInteractions.Rotamers.get(keyPdbCodeAA2)+" oldRotamers1: "+AllRotamerInteractions.oldRotamers.get(keyPdbCodeAA1)+" oldRotamers2: "+AllRotamerInteractions.oldRotamers.get(keyPdbCodeAA2));
//                                    System.out.println(a1InteractionsNew+" + ("+a2InteractionsNew+" - "+AllRotamerInteractions.getInteraction(keyPdbCodeAA1, AllRotamerInteractions.Rotamers.get(keyPdbCodeAA1), keyPdbCodeAA2, AllRotamerInteractions.Rotamers.get(keyPdbCodeAA2))+")");
//                                }
                                if(a1InteractionsOld>50){
                                    System.out.println("\nAminoácido: "+keyPdbCodeAA2+" Rot1: "+rot+" Rot2: "+rot2+" Rotamers1:"+AllRotamerInteractions.Rotamers.get(keyPdbCodeAA1)+" Rotamers2:"+AllRotamerInteractions.Rotamers.get(keyPdbCodeAA2)+" oldRotamers1: "+AllRotamerInteractions.oldRotamers.get(keyPdbCodeAA1)+" oldRotamers2: "+AllRotamerInteractions.oldRotamers.get(keyPdbCodeAA2));
                                    System.out.println(a1InteractionsNew+" + ("+a2InteractionsNew+" - "+AllRotamerInteractions.getInteraction(keyPdbCodeAA1, AllRotamerInteractions.Rotamers.get(keyPdbCodeAA1), keyPdbCodeAA2, AllRotamerInteractions.Rotamers.get(keyPdbCodeAA2))+")");
                                }
                                auxEnergy=AllRotamerInteractions.getTotalEnergyEfficient()+newTotSelfE;
                                obj[0] = rot;
                                obj[1] = Boolean.TRUE;
                                change1 = true;
                                
                                a1InteractionsOld = a1InteractionsNew;
//                                a2InteractionsOld = a2InteractionsNew;
                                AllRotamerInteractions.AAInteractions.put(keyPdbCodeAA1, a1InteractionsNew);
                                TotEnergyOld = TotEnergyNew;
                                AllRotamerInteractions.selfEnergies.put(keyPdbCodeAA1, newSelfE1);
                                AllRotamerInteractions.totalSelfEnergy = newTotSelfE;
                                AllRotamerInteractions.oldRotamers.put(keyPdbCodeAA1, rot);
                                AllRotamerInteractions.oldRotamers.put(keyPdbCodeAA2, rot2);
                                //oldRotamer mal??? porque si guarda zero cuando new energy ==0
                                AllRotamerInteractions.selfEnergies.put(keyPdbCodeAA2 , newSelfE2);
                                AllRotamerInteractions.AAInteractions.put(keyPdbCodeAA2, a2InteractionsNew);
                                
                                rotameros.put(keyPdbCodeAA2, rot2);
                                
                            }
                        }
                        
                        
//                        if (change1) {
//                            aa2 = StructureUtil.createAminoacid(aa2, rl2.getRotamer().get(rotameros.get(keyPdbCodeAA2)));
//                            actualizarGrid(aa2, aa2,rotameros.get(Integer.parseInt(aa2.getPDBCode())));
//                        }
                        if(rotameros.containsKey(keyPdbCodeAA2)){
                            AllRotamerInteractions.Rotamers.put(keyPdbCodeAA2, rotameros.get(keyPdbCodeAA2));
                        }else{
                            AllRotamerInteractions.Rotamers.put(keyPdbCodeAA2, Integer.parseInt(AllRotamerInteractions.sol.getRomaterVector().get(0).get(keyPdbCodeAA2)));
                        }
                    }
                }
            }
            
            

        }
        
        if (!collision) {
                try {
                    for (int rotTemp = 0; rotTemp < rotsEnabled1.size(); rotTemp++) {
                        rot = rotsEnabled1.get(rotTemp);

                        aa1 = StructureUtil.createAminoacid(aa, rl.getRotamer().get(rot));
                        actualizarGrid(aa, aa1, rot);

                        a1InteractionsOld = AllRotamerInteractions.AAInteractions.get(keyPdbCodeAA1);

                        a1InteractionsNew = AllRotamerInteractions.getAminoacidInteractions(keyPdbCodeAA1);
//                        if(keyPdbCodeAA1==1 || keyPdbCodeAA1==28){
//                            System.out.println("Interacción ** en "+keyPdbCodeAA1+" con "+rot+": "+AllRotamerInteractions.AAInteractions.get(28));
//                        }

                        TotEnergyNew = TotEnergyOld - a1InteractionsOld + a1InteractionsNew;
                        newSelfE1 = AllRotamerInteractions.aGrid.getRotamerFrequencyRASP(aa1.getPDBName(), rot);
                        newTotSelfE = AllRotamerInteractions.totalSelfEnergy - AllRotamerInteractions.selfEnergies.get(keyPdbCodeAA1) + newSelfE1;
                        //************************************/
//                        TotEnergyNew=AllRotamerInteractions.getTotalEnergyEfficient();
                        
                        if ((TotEnergyNew + (newTotSelfE*0.35)) < (TotEnergyOld + (AllRotamerInteractions.totalSelfEnergy*0.35)) /*&& AllRotamerInteractions.getTotalEnergyEfficient()<auxEnergy*/) {
                            System.out.println("*"+TotEnergyNew+" + " + newTotSelfE+" < "+TotEnergyOld+" + " + AllRotamerInteractions.totalSelfEnergy+" *"+" -- VS "+(AllRotamerInteractions.getTotalEnergyEfficient()+newTotSelfE));
                            if((TotEnergyNew + newTotSelfE+30) < (TotEnergyOld + AllRotamerInteractions.totalSelfEnergy)){
                                System.out.println("***"+TotEnergyNew+"="+TotEnergyOld+"-"+a1InteractionsOld+"+"+a1InteractionsNew+" en "+keyPdbCodeAA1+" con "+rot+" y antiguo rot: "+AllRotamerInteractions.oldRotamers.get(keyPdbCodeAA1));
                            }
                            auxEnergy=AllRotamerInteractions.getTotalEnergyEfficient();
                            obj[0] = rot;
                            obj[1] = Boolean.TRUE;
                            change2 = true;
                            AllRotamerInteractions.oldRotamers.put(keyPdbCodeAA1, rot);
                            AllRotamerInteractions.AAInteractions.put(keyPdbCodeAA1, a1InteractionsNew);
                            TotEnergyOld = TotEnergyNew;
                            AllRotamerInteractions.selfEnergies.put(keyPdbCodeAA1, newSelfE1);
                            AllRotamerInteractions.totalSelfEnergy = newTotSelfE;
                        }
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                    System.out.println("NULLL!!");
                }
            }
        


        if (change2) {
            aa1 = StructureUtil.createAminoacid(aa, rl.getRotamer().get(Integer.parseInt(obj[0].toString())));
            actualizarGrid(aa, aa1, Integer.parseInt(obj[0].toString()));
        } else {
//            aGrid = StructureUtil.actualizarGrid(aGrid, aminoacidMap, aa);
        }
        if(keyPdbCodeAA1==1){
                System.out.println(" **** ANTES ***");
                ari.getTotalEnergyEfficientDetailed();
                System.out.println("\n\n");
        }

        if (change1) {
            aa1 = StructureUtil.createAminoacid(aa, rl.getRotamer().get(Integer.parseInt(obj[0].toString())));
            actualizarGrid(aa, aa1, Integer.parseInt(obj[0].toString()));
        }
        obj[3] = rotameros;

        return obj;
    }

    @SuppressWarnings("CallToThreadDumpStack")
    public static Solution actualizarAminoacidos(Solution sol, HashMap<Integer, Integer> rotameros) {
        Object[] keys = rotameros.keySet().toArray();
        Arrays.sort(keys);
        AminoAcid aa, aa1;
        AminoacidExtended aex;
        int KeyPDBCodeAA = 0, rot = 0;

        try {
            for (int i = 0; i < rotameros.size(); i++) {
                KeyPDBCodeAA = Integer.parseInt(keys[i].toString());
                aex = AllRotamerInteractions.aGrid.aminoacidMap.get(KeyPDBCodeAA+1);
                aa = aex.getAminoacid();
                rl = rlc.getRotamerLibrary(aa.getPDBName());
                rot = rotameros.get(Integer.parseInt(keys[i].toString()));
                aa1 = StructureUtil.createAminoacid(aa, rl.getRotamer().get(rot));
//            System.out.println("Aminoacido: "+keys[i].toString()+" rotámero: "+(rotameros.get(Integer.parseInt(keys[i].toString()))));//falta -1?
                sol.getRomaterVector().get(0).put(KeyPDBCodeAA , rot + "");
                AllRotamerInteractions.Rotamers.put(KeyPDBCodeAA, rot);
                actualizarGrid(aa, aa1, rot);
                aex.setAminoacid(aa1);
                AllRotamerInteractions.aGrid.aminoacidMap.put(Integer.parseInt(aa.getPDBCode()), aex);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return sol;
    }

    public static void actualizarGrid(AminoAcid aa, AminoAcid aa1, int rot) {
        CellGeneric tempCell = new CellGeneric();
        String tempCellID = "";
        String pdbName =aa.getPDBName();
        int aminoKey = Integer.parseInt(aa.getPDBCode()) - 1;

        ArrayList<Atom> atoms = new ArrayList<>();
        atoms = AminoacidsGrid.getCentralAtomAndMoreDistantAtom(aa1);
        AminoacidExtended aext2 = new AminoacidExtended(aa, atoms.get(0), atoms.get(1), ListUtil.aminoRadius(pdbName), "", AllRotamerInteractions.aGrid.getRotamerFrequencyRASP(pdbName, rot));

        AllRotamerInteractions.Rotamers.put(aminoKey, rot);


        ArrayList<Atom> tempAtoms;

        for (Atom a : aa.getAtoms()) {
            tempCellID = AllRotamerInteractions.aGrid.getStringKeyCell(interval, a.getCoords());
            tempCell = AllRotamerInteractions.aGrid.getGrid().get(tempCellID);
//            System.out.println("Is tempCell != null? :"+tempCell!=null);
            tempAtoms = tempCell.getElements();
//            System.out.println("Atomos en celda: "+tempAtoms.size());
            for (int i = 0; i < tempAtoms.size(); i++) {
                if (tempAtoms.get(i).getParent().getPDBCode().equals(a.getParent().getPDBCode())) {
                    tempAtoms.remove(tempAtoms.get(i));
                }
            }
//            for(Atom atomInCell: tempAtoms){
//                if(atomInCell.getParent().getPDBCode().equals(a.getParent().getPDBCode())){
//                    tempAtoms.remove(atomInCell);
//                }
//            }
            tempCell.setElements(tempAtoms);
            AllRotamerInteractions.aGrid.getGrid().put(tempCellID, tempCell);
        }

        for (Atom a1 : aa1.getAtoms()) {
            tempCellID = AllRotamerInteractions.aGrid.getStringKeyCell(interval, a1.getCoords());
            tempCell = AllRotamerInteractions.aGrid.getGrid().get(tempCellID) != null ? AllRotamerInteractions.aGrid.getGrid().get(tempCellID) : new CellGeneric();
            tempAtoms = tempCell.getElements();
            tempAtoms.add(a1);
            tempCell.setElements(tempAtoms);
            AllRotamerInteractions.aGrid.getGrid().put(tempCellID, tempCell);
        }

        AllRotamerInteractions.aGrid.aminoacidMap.put(aminoKey+1, aext2);
    }

    @SuppressWarnings("CallToThreadDumpStack")
    public static void main(String[] args) throws IOException {
        initDirectories();
        String pdbList = "lista";// TODO


        ArrayList<String> alPDBIds = PDBReader.readPDBList(pdbList);
        PrintWriter outFile = null, outFileTimeCisrr = null;
        File fileOut = null, fileOutTimeCisrr = null;
        Structure s1 = null, s3 = null, sb = null;
        Solution sol;


        fileOut = new File(PathAlgorithm + "AllEnergyFunctions.txt");
        outFile = new PrintWriter(new BufferedWriter(new FileWriter(fileOut)));
        fileOutTimeCisrr = new File(PathAlgorithm + "TimecisRR.txt");
        outFileTimeCisrr = new PrintWriter(new BufferedWriter(new FileWriter(fileOutTimeCisrr)));

        outFile.println("vdW Potential - Cratio - Rotamer Frequency ");
        outFileTimeCisrr.println("PDB\t#Aminoacidos\tTiempo");


        try {
            for (int i = 0; i < alPDBIds.size(); i++) {
                outFile.println((i + 1) + ": " + alPDBIds.get(i));
                s1 = PDBReader.readStructure(alPDBIds.get(i));
                sol = new Solution(s1);
//                StructureUtil.createBestStructure(s1, sol, rlc);
                //sol.Generate_RandomRotamers(rnd);
                sol.setMaxProb();
                sb = StructureUtil.createStructure(s1, sol, rlc); // sb=s1??
//                double ebefore = AllRotamerInteractions.getTotalEnergyEfficient(sol);   //#ChangeCol        Done
                outFile.println("Best :  vdW : " + sol.vdWPotential + " Cratio : " + sol.cRatio + " Rotamer Frequency : " + sol.frequency);

                int nresidue = getSeqResLength(s1);
                long time = System.currentTimeMillis();
                System.out.println("Star CisRR to " + alPDBIds.get(i) + " with " + nresidue + " residue");
                //StructureUtil.cisRR(sol, s1, rlc, efficientColl);  //#ChangeCol        Done
                // StructureUtil._cisRR(sol, s1, rlc, true); //new line
                //algoritmo  #####################################
                TS(sol, s1, rlc, true);

                time = System.currentTimeMillis() - time;
                System.out.println("end CisRR, duration(ms) : " + time);


                String pr = alPDBIds.get(i) + "\t" + nresidue + "\t" + time;
                outFileTimeCisrr.println(pr);

                s3 = StructureUtil.createStructure(s1, sol, rlc);

                outFile.println("Best+CisRR :  vdW : " + sol.vdWPotential + " Cratio : " + sol.cRatio + " Rotamer Frequency : " + sol.frequency);
                PDBWrite.writePDB(s3, PathAlgorithm + "BestC/" + alPDBIds.get(i) + ".out");
                PDBWrite.writePDB(sb, PathAlgorithm + "Best/" + alPDBIds.get(i) + ".out");
                PDBWrite.writePDB(s3, PathAlgorithm + "BestC" + alPDBIds.get(i) + ".pdb");
                PDBWrite.writePDB(sb, PathAlgorithm + "Best" + alPDBIds.get(i) + ".pdb");
                StructureUtil.CollisionAnalysis(PathAlgorithm + alPDBIds.get(i) + "BESC.col", s3);
                StructureUtil.CollisionAnalysis(PathAlgorithm + alPDBIds.get(i) + "BES.col", sb);

                outFile.flush();
                outFileTimeCisrr.flush();


            }
            outFile.close();
            outFileTimeCisrr.close();

            outputFiles(alPDBIds, true);
        } catch (Exception ex) {
//            System.err.print(ex);
            ex.printStackTrace();
        }
    }

    public static int getSeqResLength(Structure st) {
        int numberofAmino = 0;
        for (Chain c : st.getChains()) {
            numberofAmino += c.getAtomGroups().size();
        }

        return numberofAmino;
    }

    private static void outputFiles(ArrayList<String> alPDBIds, boolean efficientColl) {
        StringBuilder sb = new StringBuilder(), sb2 = new StringBuilder(), sbrError = new StringBuilder(), sbPotential = new StringBuilder(), sbcRatio = new StringBuilder();
        double relativeError, potential = 0.0, pdbPotential = 0.0, cratio = 0.0, pdbCratio = 0.0;
        Structure s = null;
        String pdbId;
        String[] methods = new String[]{"Best", "BestC"};
        String[] measures = new String[]{"X1", "X12", "X123"};
        Structure pdbStructure, methodStructure;
        DecimalFormat df = new DecimalFormat("#0.0000");
        DecimalFormat df1 = new DecimalFormat("#0.0000000000");
        try {
            //Exports the files RelativeError, vdWPotential and Cratio
            File fileRelativeError = new File(PathAlgorithm + "RelativeError.txt");
            PrintWriter outFile = new PrintWriter(new BufferedWriter(new FileWriter(fileRelativeError)));
            File fileOut2 = new File(PathAlgorithm + "vdWPotential.txt");
            PrintWriter outFile2 = new PrintWriter(new BufferedWriter(new FileWriter(fileOut2)));
            outFile.println("AA\t\tBest\t\tBestC");
            outFile2.println("AA\t\tBest\t\tBestC");
            File fileOut3 = new File(PathAlgorithm + "Cratio.txt");
            PrintWriter outFile3 = new PrintWriter(new BufferedWriter(new FileWriter(fileOut3)));
            outFile3.println("AA\t\tBest\t\tBestC");
            System.out.println("\nRelativeError ");
            for (int i = 0; i < alPDBIds.size(); i++) {
                pdbId = alPDBIds.get(i);
                System.out.println("Protein : " + pdbId);
                s = PDBReader.readStructure(pdbId);
                pdbPotential = StructureUtil.getvdWPotential(s); //#ChangeCol       Done
                pdbCratio = StructureUtil.getCRatio(s);          //#ChangeCol       Done
                sbrError.append(pdbId);
                sbPotential.append(pdbId);
                sbcRatio.append(pdbId);
                for (int j = 0; j < methods.length; j++) {
                    potential = StructureUtil.getvdWPotential(StructureUtil.readStructure(alPDBIds.get(i), methods[j], PathAlgorithm)); //#ChangeCol       Done
                    cratio = StructureUtil.getCRatio(StructureUtil.readStructure(alPDBIds.get(i), methods[j], PathAlgorithm));          //#ChangeCol       Done
                    relativeError = ((potential - pdbPotential) / pdbPotential) * 100;
                    sbrError.append("\t\t").append(df.format(relativeError));
                    sbPotential.append("\t\t").append(df.format(potential));
                    sbcRatio.append("\t\t").append(df1.format(cratio));
                }
                outFile.println(sbrError.toString());
                sbPotential.append("\t\t").append(df.format(pdbPotential));
                outFile2.println(sbPotential.toString());
                sbcRatio.append("\t\t").append(df1.format(pdbCratio));
                outFile3.println(sbcRatio.toString());

                sbPotential = new StringBuilder();
                sbcRatio = new StringBuilder();
                sbrError = new StringBuilder();

                outFile.flush();
                outFile2.flush();
                outFile3.flush();
            }
            outFile.close();
            outFile2.close();
            outFile3.close();

            //Exports the file Absolute Accuracy
            File fileAbsAccuracy = new File(PathAlgorithm + "Absolute Accuracy.txt");
            File fileConcentrated = new File(PathAlgorithm + "Absolute Accuracy(concentrated).txt");
            outFile = new PrintWriter(new BufferedWriter(new FileWriter(fileAbsAccuracy)));
            outFile2 = new PrintWriter(new BufferedWriter(new FileWriter(fileConcentrated)));
            System.out.println("\nAbsolute Accuracy ");
            outFile2.println("\t\t\tBest\t\t\t\t\tBestC");
            outFile2.println("PDB\tX1\tX1+2\tX1+2+3\tRSMD\tNumCol\tX1\tX1+2\tX1+2+3\tRSMD\tNumCol \n");
            for (int i = 0; i < alPDBIds.size(); i++) {
                pdbId = alPDBIds.get(i);
                outFile.println("\n\nProtein : " + pdbId);
                outFile2.print(pdbId);
                System.out.println("Protein : " + pdbId);
                outFile.println("METHOD\t\tX1\tX1+2\tX1+2+3\tRSMD\tNo.Colisions");
                pdbStructure = PDBReader.readStructure(pdbId);
                for (int j = 0; j < methods.length; j++) {
                    sb.append(methods[j] + "\t");
                    methodStructure = StructureUtil.readStructure(pdbId, methods[j], PathAlgorithm);
                    int icollision = StructureUtil.NumberOfCollision(methodStructure);      //#ChangeCol    Done
                    for (int z = 0; z < measures.length; z++) {
                        sb.append("\t").append(df.format(StructureUtil.AbsoluteAccuracy(pdbStructure, methodStructure, measures[z])));
                        sb2.append("\t").append(df.format(StructureUtil.AbsoluteAccuracy(pdbStructure, methodStructure, measures[z])));
                    }
                    sb.append("\t").append(df.format(StructureUtil.RMSD(pdbStructure, methodStructure)));
                    sb.append("\t").append(icollision);
                    sb2.append("\t").append(df.format(StructureUtil.RMSD(pdbStructure, methodStructure))).append("\t").append(icollision);
                    outFile.println(sb.toString());
                    outFile2.print(sb2.toString());
                    sb = new StringBuilder();
                    sb2 = new StringBuilder();
                }
                outFile2.println("");
                outFile.println("---------------------------------------------------------------------");
                outFile.flush();
                outFile2.flush();
            }
            outFile.close();
            outFile2.close();
            if (efficientColl) {
                System.out.println("######## efficientCol ########");
            } else {
                System.out.println("######## originalCol ########");
            }
        } catch (IOException ex) {
            Logger.getLogger(TabuSearch.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    public static void initDirectories() {
        String Best = PathAlgorithm + "Best";
        String BestC = PathAlgorithm + "BestC";
        File directory = new File(PathAlgorithm);

        try {
            directory.mkdirs();
            new File(Best).mkdir();
            new File(BestC).mkdir();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
